/*
 * CyberOrg.java
 *
 * Created on April 28, 2005, 11:51 AM
 */

package aa.core;

import java.util.LinkedList;
import java.util.Random;
import java.io.Serializable;
/**
 * This class represents a cyberorg
 * @author  xinghuizhao
 */
public class CyberOrg implements Serializable{
    
	
    protected long m_lTicks; //resources in this cyberorg
    protected long m_lTicksRate; //the rate of the resources in this cyberorg       
                               //resources this cyberorg could receive in one schedule cycle
    
    
    
    protected long m_lECash; //eCash
    
    protected LinkedList m_llActors; //actors in this cyberorg
    protected transient LinkedList m_llChildren; //child cyborgs
    
    protected transient CyberOrg m_cybParent; 
    
    protected ActorName m_anFacilitator; //name of the facilitator actor
    
    protected Contract m_conContract; //the contract between this cyberorg and its parent
    
    
    protected transient Platform m_pPlatform; //reference to the platform
    
    /* keep the info of facilitator, they are used in isolation */
    protected String m_strFacilitatorClass; 
    protected Object[] m_objaAgs;
    
    protected long m_lPrice; // current price of resouce (per tick)
    
    protected boolean m_bIsActive; //it is true if the cyberorg still has some ticks 
    
    protected long m_lTimesliceAssigned; //record the total time slice that was assigned to actors
                                         //it is used to update the remainning ticks in the cyberorg after every scheduler round
    
    public long height;
    
    
    
    /** Creates a new instance of CyberOrg */
    public CyberOrg() {
    }
    
    /** Creates a new instance of CyberOrg with known resource specification
     * 
     * */
    public CyberOrg(Long p_longTicks, Long p_longTicksRate, Long p_longECash, String p_strFacilitatorClass, Object[] p_objaAgs){
        
        /**
         * Set the values of member variabls
         */
        
        m_lTicks = p_longTicks.longValue();
        
        m_lTicksRate = p_longTicksRate.longValue();
        
        m_lECash = p_longECash.longValue();
        
        m_lPrice = 5;
        
        m_bIsActive = true;
        
        m_cybParent = null; //set parent later if it is not root cyberorg
        m_conContract = new Contract(p_longTicks.longValue(),p_longTicksRate.longValue(),p_longECash.longValue());
        
        m_llActors = new LinkedList();
        
        m_llChildren = new LinkedList();
        
        m_pPlatform = Platform.getPlatform();
        
        m_strFacilitatorClass = p_strFacilitatorClass;
        
        m_objaAgs = new Object[p_objaAgs.length];
        
        
        
        for (int i=0;i<p_objaAgs.length;i++){
            m_objaAgs[i] = p_objaAgs[i];
        }
        
        
        /**
         * Create Facilitator actor for this cyberorg
         */
        
        Object[] obja = new Object[p_objaAgs.length+1];
        
        obja[0] = this;
        
        for (int i=1;i<obja.length;i++){
            
            obja[i] = p_objaAgs[i-1];
            
        }
        
        try{
            
            m_anFacilitator = m_pPlatform.createActor(p_strFacilitatorClass, obja);
            
            m_pPlatform.registerActorCyb(m_anFacilitator, this);
            
            m_llActors.add(m_anFacilitator);
            
            resourceAlloc(m_anFacilitator);
            
        }catch (CreateActorException e){
            
            System.out.println("Exception while creating facilitator "+ e);
        }
        
        
        
        
        
    }
    
    
    /** Constructor that is used to create a cyberorg 
     * for the one which is migrating into the current cyberorg
     */
    
    public CyberOrg(Contract p_conContract,Long p_LECash,LinkedList p_llActors){
        
        m_lTicks = p_conContract.getTicks();
        m_lTicksRate = p_conContract.getTicksRate();
        m_lECash = p_LECash.longValue();
        
        m_lPrice = 5;
        
        m_anFacilitator = (ActorName)p_llActors.get(0);
        
        m_conContract = p_conContract;
        
        m_llChildren = new LinkedList(); //add child cyberorgs to it after they are migrated here
        
        m_llActors = p_llActors;
        
        
        
        m_pPlatform = Platform.getPlatform();
        
        // still need to figure out the m_strFacilitator and m_objaAgs
        
        
        
    }

        /**
     *  Checkes whether this CyberOrg is same as the specified object.
     *
     *  @param p_objOther the reference to actor name of another actor to be checked
     *
     *  @return <code>true</code> if this actor name is the same as the specified object; <br>
     *          <code>false</code> otherwise.
     */
    public boolean equals(Object p_objOther)
    {
        return(equals((CyberOrg)p_objOther));
    }


    /**
     *  Checks whether this CyberOrg is same as the specified actor name.
     *
     *  @param p_anOther the name of another actor to be checked.
     *
     *  @return <code>true</code> if this actor name is the same as the specified actor name; <br>
     *          <code>false</code> otherwise.
     */
    public boolean equals(CyberOrg p_anOther)
    {
    	if (p_anOther == null) {
    	    return false;
    	}

    	if (this.getFacilitator().getUAN().compareTo(p_anOther.getFacilitator().getUAN()) == 0) {
    	    return true;
    	} else {
    	    return false;
    	}
    }

    /**Set parent cyberorg for this cyberorg
     * it is called by isolation, migration
     * 
     * @param p_cybParent The parent cyberorg
     * */
    public void setParent(CyberOrg p_cybParent){
        m_cybParent = p_cybParent;
    }
    
    /**
     * Set up the current CyberOrgs platform
     *
     */
    public void setPlatform(){
        m_pPlatform = Platform.getPlatform();
    }
    
    /** it is called in remote migration. 
     * after this cyberorg migrates to another platform,
     * we didn't bring the child list
     * instead, we create new child list, and wait for child cyberorgs coming
     */
    public void setNullChild(){
        m_llChildren = new LinkedList();
    }
    
    /**
     * Returns to a handler of current platform
     * @return The current CyberOrgs platform
     */
    public Platform getPlatform(){
        return m_pPlatform;
    }
    
    /**
     * Set up the height of current cyberorg
     * @param p_lHeight The height of current cyberorg
     */
    public void setHeight(long p_lHeight){
        height=p_lHeight;
    }
    
    /**
     * Returns the height of current cyberorg
     * @return The height of current cyberorg
     */
    public long getHeight(){
        return height;
    }
    
    /**
     * CyberOrg isolate <br>
     * This method is called by the facilitator
     * 
     * @param p_lECash The eCash that is given to the new child cyberorg
     * @param p_anaActors A list of actors that will be isolated to the child cyberorg
     * @param p_conContract The contract between the current cyberorg and its new child cyberorg
     * @return The newly created child cyberorg
     */
    
    public CyberOrg isolate(long p_lECash,ActorName[] p_anaActors,Contract p_conContract){
        
        /** New a child cyberorg */
        
        long ticks = p_conContract.getTicks();
        
        long ticksRate = p_conContract.getTicksRate();

        CyberOrg newChild = new CyberOrg(new Long(ticks),new Long(ticksRate),new Long(p_lECash),m_strFacilitatorClass,m_objaAgs);
        m_pPlatform.registerCyberOrg(newChild);
        
        newChild.setHeight(height+1);
        
        //System.out.println("in CyberOrg class: child height:  "+ getHeight());
        
        /** Update the actor list for both parent and child cyberorg
         * Parent cyberorg takes the resource back from the actors which are isolated to the new child
         * The child cyberorg reallocate resources according to the local policies
         */
        
        for (int i=0;i<p_anaActors.length;i++){
            
            
            m_llActors.remove(p_anaActors[i]);
            
            long resource = m_pPlatform.returnRes(p_anaActors[i]);
            
            
            
            m_lTicksRate += resource; //return the ticksRate to old host
            
            newChild.insertActor(p_anaActors[i]);
            
            
            
            newChild.resourceAlloc(p_anaActors[i]); //start receiving ticksRate from the new cyberorg
        }
        
        
        /* update the child list*/
        m_llChildren.add(newChild);
        
        /* set parent for the child*/
        newChild.setParent(this);
        
        return newChild;
        
        
    }
    
    /**
     *  Insert an actor to the actors list
     *  
     * @param p_anToInsert The name of the actor which is added to the current actor list
     */
    public void insertActor(ActorName p_anToInsert){
        m_llActors.add(p_anToInsert);
        updatePrice();
    }
    
    
    /** 
     * Default resource allocation policy for actors in this cyberorg <br>
     * User can define their own resource allocation policy by overwriting this method
     * 
     * @param p_anNeedResource The name of the actor for which resources are allocated
     * 
     */ 
    
    public void resourceAlloc(ActorName p_anNeedResource){
        
        // generate the percentage of resource
        Random ran = new Random();
        boolean b=false;
        
        //for(int i=0; i<5; i++){
        
            //Dec 2nd 2005, instead of using percentage, we generate the time slices directly.
            //int percent = ran.nextInt(50);
        
        
            //long rateAssign = m_lTicksRate*percent/100;
            long rateAssign = ran.nextInt(200);
            rateAssign += 2;
            m_lTicksRate -= rateAssign;
            
            //allocate the resource
            
            //b = m_pPlatform.resAlloc(p_anNeedResource, rateAssign);
            
            Object[] ags = new Object[2];
            ags[0]=p_anNeedResource;
            ags[1]=new Long(rateAssign);
            
            ActorMessage amResAlloc = new ActorMessage(getFacilitator(),
                                                       m_pPlatform.getActorNameOfPlatform(),
                                                       "resAlloc", ags,false);
            
            m_pPlatform.sendMessage(amResAlloc);
            
            /*if (b) {
                //m_lTimesliceAssigned += rateAssign;
                //break;
            }*/
        //}
        
        /*if (!b){
            if (m_lTicksRate>2){
                m_pPlatform.resAlloc(p_anNeedResource, 2);
            }
            else{
                System.err.println("CyberOrg "+this +"runs out of TicksRate: "+m_lTicksRate);
            }
        }*/
        
    }
    
    /**
     * Another resource allocation policy which assigns a known amount of resource to the given actor
     * 
     * @param p_anNeedResource The name of the actor for which resources are allocated
     * @param p_lRate Amount of resource which is assigned to the given actor
     */
    public void resourceAlloc(ActorName p_anNeedResource, long p_lRate){
       
        try {
            long originalRes = m_pPlatform.returnRes(p_anNeedResource);
        
            m_lTicksRate += originalRes;
            
        }catch (Exception e){
            
        }
        
        //put this statement temporarily in order to avoid timeslice being 0
        //if (p_lRate<=0) p_lRate = 2; 
        
        
        m_lTicksRate -= p_lRate;
          
        
        //allocate the resource
        //comment for debug
        m_pPlatform.resAlloc(p_anNeedResource, p_lRate);
        
    }
    
    /** Allocate resource for all actors
     * It is called after migration (local)
     */
    public void resourceAllocForAll(){
        for(int i=0;i<m_llActors.size();i++){
            resourceAlloc((ActorName)m_llActors.get(i));
        }
    }
    
    /**
     * CyberOrg assimilate
     * @return The parent cyberorg
     */
    public CyberOrg assimilate(){
        
        /** return resources, actors, and eCash to parent */
        
        m_cybParent.getResBack(m_lTicks,m_lTicksRate,m_lECash);
        
        /** reallocate resource for all actors in the assimilating cyberorg 
         * and update parent's actors list*/                     
        
        for (int i=0;i<m_llActors.size();i++){
            ActorName theActor = (ActorName)m_llActors.get(i);
            m_cybParent.insertActor(theActor);
            m_cybParent.resourceAlloc(theActor);
        }
        
        
        
        /** updates parent's child list */
        m_cybParent.deleteChild(this);
        m_pPlatform.removeCyberOrg(this);
        
        return m_cybParent;
        
    }
    
    /** 
     * Get resource back from a child cyberorg which is assimilating<br> 
     * called by assimilation of child cyberorg
     * 
     * @param p_lTicks Resources that are returned to the current cyberorg
     * @param p_lTicksRate Resource rate that is returned to the current cyberorg
     * @param p_lECash eCash that is returned to the current cyberorg
     */
    public void getResBack(long p_lTicks, long p_lTicksRate, long p_lECash){
        
        m_lTicks += p_lTicks;
        m_lTicksRate += p_lTicksRate;
        m_lECash += p_lECash;
        
    }
    
    /** 
     * Delete a child cyberorg from the child list, it is called in assimilation
     * 
     * @param p_cybChild The child cyberorg which is assimilating
     */
    public void deleteChild(CyberOrg p_cybChild){
        m_llChildren.remove(p_cybChild);
    }
    
    /**
     * Add a child cyberorg to the current child list
     * 
     * @param p_cybChild The new child cyberorg
     */
    public void addChild(CyberOrg p_cybChild){
        m_llChildren.add(p_cybChild);
    }
    
    /** 
     * Delete an Actor when it is distroyed
     * 
     * @param p_anActor The name of the actor to be deleted
     */
    
    public void deleteActor(ActorName p_anActor){
        m_llActors.remove(p_anActor);
    }
    
    /**
     * CyberOrg migrate
     * 
     * @param p_anDestFacilitator The ficilitator actor of the destination parent cyberorg
     * @param p_conNewContract The new contract between the current cyberorg and its new parent
     */
    public void migrate(ActorName p_anDestFacilitator,Contract p_conNewContract){
        
        //return resources to current parent
        m_cybParent.getResBack(m_lTicks, m_lTicksRate, 0);
        
        //set new contract
        m_conContract = p_conNewContract;
        m_lTicks = p_conNewContract.getTicks();
        m_lTicksRate = p_conNewContract.getTicksRate();
        m_lECash -= p_conNewContract.getPrice();
        
        //delete this cyberorg from parent's child list
        m_cybParent.deleteChild(this);
        
        /** if the destination cyberorg is on current platform, call local migrate,
         * otherwise call remote migrate*/
        String destAddress = p_anDestFacilitator.getLocationAddress();
        String localAddress = m_pPlatform.getHostAddress();
        if (destAddress.equals(localAddress)){
                    
            //local migrate
            m_pPlatform.localMigrate(this,p_anDestFacilitator,p_conNewContract);
            
        }
        else{
            
            //remote migrate
            m_pPlatform.remoteMigrate(this,p_anDestFacilitator,p_conNewContract);
            
        }
        
    }
    
    /** 
     * Returns the name of the facilitator actor
     * 
     * @return The name of the facilitator actor
     */
    public ActorName getFacilitator(){
        return m_anFacilitator;
    }
    
    /**
     * Returns the number of eCash in the current cyberorg
     * @return the number of eCash in the current cyberorg
     */
    public long getECash(){
        return m_lECash;
    }
    
    /**
     * Returns the actor list
     * @return The actor list of the current cyberorg
     */
    public LinkedList getActors(){
        return m_llActors;
    }
    
    /**
     * Returns the child list of the current cyberorg
     * @return The child list
     */
    public LinkedList getChildren(){
        return m_llChildren;
    }
    
    /**
     * Returns the contract
     * @return The contract between the current cyberorg and its parent
     */
    public Contract getContract(){
        return m_conContract;
    }
    
    /**
     * Returns the amount of resources the current cyberorg has
     * @return The amount of resources
     */
    public long getTicks(){
        return m_lTicks;
    }
    
    /**
     * Returns the resource rate of the current cyberorg
     * @return Resource rate
     */
    public long getTicksRate(){
        return m_lTicksRate;
    }
    
    /**
     * Returns the current resource price in the current cyberorg
     * @return Price of its resources
     */
    public long getPrice(){
        return m_lPrice;
    }
    
    /** 
     * Receive a CyberOrg <br>
     * This method is called when remote migration happens
     * create a cyberorg according to the parameters we recived
     * 
     * @param p_conContract The contract in the cyberorg that is being received
     * @param p_LECash Number of eCash
     * @param p_llActors Actor list 
     */
    
    public void receiveCyberOrg(Contract p_conContract,Long p_LECash,LinkedList p_llActors){
        /* create new cyberorg according to parameters */
        
        CyberOrg newChild = new CyberOrg(p_conContract,p_LECash,p_llActors);
        
        /* put the new cyberorg to child list*/
        
        m_llChildren.add(newChild);
        
        newChild.setParent(this);
        newChild.setPlatform();
        
        /* register the new cyberorg's actors to CyberOrgManager
         * so that we can schedule those actors after they arrives
         */
        for (int i=0;i<p_llActors.size();i++){
            ActorName an = (ActorName)p_llActors.get(i);
            an.setLocationAddress(m_pPlatform.getHostAddress());	
            m_pPlatform.registerActorCyb(an.toString(),newChild);
            
        }
        
        System.out.println("extracted Cyberorog: " +System.currentTimeMillis());
        
        
    }
    
    /**
     *  Receive a CyberOrg without creating a new instance
     *  
     * @param p_conContract Contract in the cyberorg being received
     * @param p_LECash Number of eCash
     * @param p_llActors Actor list
     * @param p_cybMigrating A handler to the cyberorg in migration
     */
    public void receiveCyberOrg(Contract p_conContract,Long p_LECash,LinkedList p_llActors,CyberOrg p_cybMigrating){
        
        
        m_llChildren.add(p_cybMigrating);
        
        p_cybMigrating.setParent(this);
        p_cybMigrating.setPlatform();
        p_cybMigrating.setNullChild();
        
        /* register the new cyberorg's actors to CyberOrgManager
         * so that we can schedule those actors after they arrives
         */
        for (int i=0;i<p_llActors.size();i++){
            ActorName an = (ActorName)p_llActors.get(i);
            an.setLocationAddress(m_pPlatform.getHostAddress());	
            m_pPlatform.registerActorCyb(an.toString(),p_cybMigrating);
            m_pPlatform.registerActorCyb(an,p_cybMigrating);
            
        }
        
        
    }
    
    /** 
     * Update Actors List, which is called in remote migration (when actors are migraing here)
     * 
     * @param p_anActorName The name of the actor being added to the current cyberorg
     */
    
    public void updateActors(ActorName p_anActorName){
        for (int i=0; i<m_llActors.size();i++){
            if (((ActorName)m_llActors.get(i)).getUAN().equals(p_anActorName.getUAN())){
                m_llActors.remove(i);
                m_llActors.add(i, p_anActorName);
                break;
            }
        }
    }
    
    /** toString for debug */

    @Override
    public String toString(){
        return "Contract:"+m_conContract+" Facilitator Class:"+m_strFacilitatorClass+" Args: "+ m_objaAgs;
    }
    
    public void printInfo(){
        System.out.println("CyberOrg...info...");
        System.out.println("Contract:"+m_conContract);
        System.out.println("Facilitator Class:"+m_strFacilitatorClass);
        System.out.println("Args: "+ m_objaAgs);
    }
    
    /**
     * Oct 17th, 2005.
     * Update system to adapt dynamic prices. 
     * Later on, resource discovery will be based on the price of resource (market evaluation)
     *
     * This method is used to make a local decision about the price of the resource 
     * It is called whenever a change in resource allocation happens in this cyberorg
     *
     */
    
    /*public long updatePrice(){
        long total = m_conContract.getTicks();
        double busy = (total-m_lTicks)/total;
        int actornum = m_llActors.size();
        if (busy >= 0.8){
            m_lPrice += m_lPrice *2;
            
        }
        else{
            m_lPrice *= 0.5;
        }
        return m_lPrice;
    }*/
    
    public long updatePrice(){
        m_lPrice += 1;
        return m_lPrice;
    }
    
    /**
     * This method is called by the scheduler after every scheduling cycle,
     * in order to keep track of total resource inside this CyberOrg
     * if a CyberOrg runs out of resource, it couldn't be scheduled until
     * it gets resources from somewhere else 
     */
    public long chargeTotalTicks(){
        
        long cost = m_conContract.getTicksRate()- m_lTicksRate;
        //System.out.println("chargeTotalTicks: con"+m_conContract.getTicksRate()+"cost: "+cost);
        if (m_bIsActive){
        
            
            if (m_lTicks>cost){ //enough resource
                m_lTicks -= cost;
                return cost;
            }
            else{
                System.out.println("CyberOrg runs out of resource!!!!!!!!!!!.......");
                m_pPlatform.disactiveCyberOrg(this);
                return 0;
            }
        } 
        
        else{
            if(m_lTicks > cost){
                m_bIsActive = true;
                m_pPlatform.reactiveCyberOrg(this);
                return 0;
            }
        }
        
        return 0;
        
    }
    
    /**
     * This method is used to look for the best destination CyberOrg in migration
     * @return A handler to the potential parent CyberOrg
     */
    public CyberOrg findBestDestination(){
        return m_pPlatform.findBestDestination();
    }
}
